package ch.unifr.diuf.webeng.taskmap.db;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

public class TaskMapDAO {
	private Mongo m;
	private static DB db;
	private static TaskMapDAO instance;
	
	private TaskMapDAO(){
		super();
	}
	
	public static TaskMapDAO getInstance(){
		if(instance ==null){
			instance =  new TaskMapDAO();
		}
		return instance;
	}

	public DB getDb() {
		if(db!=null){
			return db;
		}
		try {
			m = new Mongo("localhost", 27017);
			db = m.getDB("taskMap4");
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MongoException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return db;
	}
	
	/**
	 * Method that allows registration only to user with unique user name.
	 * @param userName
	 * @param password
	 * @return true if the registration was successful, false otherwise
	 */
	public boolean registerUser(String userName, String password){
		boolean successfulReg = false;

		//get the users collection (table)
		DBCollection userColl = getDb().getCollection("users");
		
		//query to check whether there is already a user with such user name
		BasicDBObject query = new BasicDBObject();
		query.put("userName", userName);
		DBObject existingUser = userColl.findOne(query);
		
		//if we find existing user with the same user name, registration is not allowed
		if(existingUser != null){
			return successfulReg;
		}
		
		successfulReg=true;
		String encryptedPass = PasswordService.getInstance().encrypt(password);
		BasicDBObject user = new BasicDBObject();
		user.put("userName", userName);
		user.put("pass", encryptedPass);
		
		
		userColl.insert(user);
		return successfulReg;
	}
	
	public String getRegisteredUser(String userName, String password){
		String encryptedPass = PasswordService.getInstance().encrypt(password);
		DBCollection userColl = getDb().getCollection("users");
		
		//query finding whether a user with such user name and password exists
		BasicDBObject query = new BasicDBObject();
		query.put("userName", userName);
		query.put("pass", encryptedPass);
		DBObject existingUser = userColl.findOne(query);
		//if no user was fond returns null
		if(existingUser ==null){
			return null;
		}
		String existingUserName= existingUser.get("userName").toString();
		return existingUserName;
	}
	public void updateTask(Task task){
		DB db = getDb();
		DBCollection coll = db.getCollection("tasksCollection");
		BasicDBObject query = new BasicDBObject();
		query.put("taskId", task.getId());
		BasicDBObject existingTask = (BasicDBObject) coll.findOne(query);
		BasicDBObject newtask = updateTaskToDoc(task, existingTask);
		coll.save(newtask);
	}
	public void saveTask(Task task){
		DB db = getDb();
		BasicDBObject taskDoc = convertTaskToDoc(task);
		
		DBCollection coll = db.getCollection("tasksCollection");
		coll.insert(taskDoc);
	}
	
	public List<Task> getTasksByUser(String user){
		List<Task> tasks = new ArrayList<Task>();
		BasicDBObject query = new BasicDBObject();
		query.put("user", user);
		
		DB db = getDb();
		DBCollection coll = db.getCollection("tasksCollection");
		
		DBCursor cursor = coll.find(query);
		while(cursor.hasNext()) {
			tasks.add(convertDocToTask(cursor.next()));
        }

		return tasks;
	}

	/**
	 * Retrieves tasks of a user for a period between 2 dates. The tasks are
	 * ordered by date from the earliest first onwards.
	 * 
	 * @param user
	 * @param beginning
	 * @param ending
	 * @return tasks of a given user between the given dates(both inclusive).
	 */
	public List<Task> getTasksBetweenDates(String user, Date beginning, Date ending){
		List<Task> tasks = new ArrayList<Task>();
		BasicDBObject query = new BasicDBObject();
		query.put("user", user);
		query.put("taskDate", new BasicDBObject("$gte", beginning).append("$lte", ending));
		
		DB db = getDb();
		DBCollection coll = db.getCollection("tasksCollection");
		
		DBCursor cursor = coll.find(query).sort(new BasicDBObject("taskDate",1));
		while(cursor.hasNext()) {
			tasks.add(convertDocToTask(cursor.next()));
        }

		return tasks;
	}
	
	//this is only a test method, returns the first task from the tasksCollection disregarding user
	public DBObject getTask(){
		DBObject o;
		DB db = getDb();
		DBCollection coll = db.getCollection("tasksCollection");
		o = coll.findOne();
		return o;
	}
	public BasicDBObject convertTaskToDoc(Task task){
		BasicDBObject taskDoc = new BasicDBObject();
		taskDoc.put("user", task.getUsername());
		taskDoc.put("taskId",task.getId());
		taskDoc.put("taskAddress", task.getTaskAddress());
		taskDoc.put("title",task.getTaskTitle());
		taskDoc.put("taskDate", task.getTaskDate());
		taskDoc.put("startTime", task.getTaskStartTime());
		taskDoc.put("endTime",	task.getTaskEndTime());
		return taskDoc;
	}
	public BasicDBObject updateTaskToDoc(Task task, BasicDBObject ob){
		ob.put("user", task.getUsername());
		ob.put("taskId",task.getId());
		ob.put("taskAddress", task.getTaskAddress());
		ob.put("title",task.getTaskTitle());
		ob.put("taskDate", task.getTaskDate());
		ob.put("startTime", task.getTaskStartTime());
		ob.put("endTime",	task.getTaskEndTime());
		return ob;
	}
	/*
	private String username;
	private int id;
	private String taskAddress;
	private Date taskDate;
	private String taskStartTime;
	private String taskEndTime;*/
	
	public Task convertDocToTask(DBObject doc){
		Task t = new Task();
		t.setUsername(doc.get("user").toString());
		t.setId((Integer)(doc.get("taskId")));
		t.setTaskTitle(doc.get("title").toString());
		t.setTaskAddress(doc.get("taskAddress").toString());
		t.setTaskDate((Date)doc.get("taskDate"));
		t.setTaskStartTime(doc.get("startTime").toString());
		t.setTaskEndTime(doc.get("endTime").toString());
		return t;
	}
}
